#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>

#define LEFT "["
#define RIGHT "]"
#define COMMAND_SIZE 1024
#define ARG_MAX 50
#define PATH_LENGTH 100
#define ENV_LENGTH 100
#define DELIM_STR " \t"

#define NO_REDIRECT     -1
#define IN_REDIRECT      0
#define OUT_REDIRECT     1
#define APPEND_REDIRECT  2

int quit = 0; //shell是否退出
int last_code = 0; //最近一次命令的退出码
char command_line[COMMAND_SIZE]; //存储输入命令
char* command_vector[ARG_MAX]; //存储解析后的输入命令
char last_env[ENV_LENGTH]; //临时存储自定义的环境变量
int is_redirect = NO_REDIRECT;
const char* filename;

const char* get_user_name()
{
  return getenv("USER");
}

const char* get_host_name()
{
  return getenv("HOSTNAME");
}

const char* get_pwd()
{
  return getenv("PWD");
}

//普通命令执行
int normalCommand(int argCunt)
{
    //fork一个子进程，并将子进程替换为欲执行命令
    pid_t id = fork();
    if(id < 0) { perror("fork err"); exit(1); }
    else if(id == 0)
    {
      if(!strcmp(command_vector[0], "ls") || !strcmp(command_vector[0], "ll"))
      {
        command_vector[argCunt++] = "--color";
        command_vector[argCunt] = NULL;
      }
      if(!strcmp(command_vector[0], "ll"))
      {
        command_vector[0] = "ls";
        command_vector[argCunt++] = "-l";
        command_vector[argCunt] = NULL;
      }
      //printf("argCount:%d", argCunt);
      //进行重定向,改变的是子进程pcb维护的fd_array[]的元素,程序替换不影响
      if(is_redirect == IN_REDIRECT)
      {
        int fd = open(filename, O_RDONLY);
        dup2(fd, 0); 
      }
      else if(is_redirect == OUT_REDIRECT)
      {
        int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
        dup2(fd, 1);
      }
      else if(is_redirect == APPEND_REDIRECT)
      {
        int fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0666);
        dup2(fd, 1);
      }
      else {}
      int ret = execvp(command_vector[0], command_vector);
      if(ret == -1) { exit(1); }
    }
    else
    { //等待子进程和获取退出信息，更新最近一次的退出信息
      int status = 0;
      waitpid(id, &status, 0);
      last_code = WEXITSTATUS(status);
    }
    return 1;
}

//内建命令执行
int buildinCommand(int argCont)
{
  if(strcmp(command_vector[0], "exit") == 0) { quit = 1; return 1; }
  if(strcmp(command_vector[0], "cd") == 0)
  {
    chdir(command_vector[1]); //跳转至指定目录
    char s[PATH_LENGTH]; 
    getcwd(s, PATH_LENGTH); //获取当前目录并更新环境变量
    sprintf(getenv("PWD"), "%s", s);
    return 1;
  }
  if(!strcmp(command_vector[0], "echo"))
  {
    if(*command_vector[1] == '$') //echo 环境变量
    {
      if(command_vector[1][1] == '?') { printf("%d\n", last_code); last_code = 0; }
      else { printf("%s\n", getenv(command_vector[1] + 1)); }
    }
    else if(*command_vector[1] == '"')
    {
      command_vector[argCont - 1][strlen(command_vector[argCont - 1]) - 1] = '\0';
      command_vector[1] = command_vector[1] + 1;
      for(int i = 1; i < argCont; ++i) { printf("%s ", command_vector[i]); } printf("\n");
    }
    else { for(int i = 1; i < argCont; ++i) { printf("%s ", command_vector[i]); } printf("\n"); }
    return 1;
  }
  if(!strcmp(command_vector[0], "export")) //export环境变量
  {
    strcpy(last_env, command_vector[1]);
    putenv(last_env);
    return 1;
  }
  last_code = 0;
  return 0;
}

void checkRedirect()
{
  for(unsigned i = 0; i < sizeof(command_line); ++i)
  {
    if(command_line[i] == '>')
    {
      command_line[i++] = '\0';
      if(command_line[i] == '>') { is_redirect = APPEND_REDIRECT; command_line[i++] = '\0'; }
      else { is_redirect = OUT_REDIRECT; }
      while(command_line[i] == ' ') { ++i; }
      filename = command_line + i;
      break;
    }
    else if(command_line[i] == '<')
    {
      command_line[i++] = '\0';
      is_redirect = IN_REDIRECT;
      while(command_line[i] == ' ') { ++i; }
      filename = command_line + i;
      break;
    }
    else {}
  }
}

//命令解析
int stringSplit()
{
  checkRedirect();//检查和判断重定向操作
  //printf("check complete, is_redirect:%d, filename:%s\n", is_redirect, filename);
  int index = 0;
  command_vector[index++] = strtok(command_line, DELIM_STR);
  if(command_vector[index - 1] != NULL) while(command_vector[index++] = strtok(NULL, DELIM_STR));
  return index - 1;
}

//用户交互
void interAct()
{
  printf(LEFT"%s@%s %s"RIGHT" ", get_user_name(), get_host_name(), get_pwd());
  fgets(command_line, COMMAND_SIZE, stdin);
  command_line[strlen(command_line) - 1] = '\0';
}

void mini_shell_init()
{
  is_redirect = NO_REDIRECT;
  filename = NULL;
}

int main()
{
  while(!quit)
  {
    mini_shell_init();
    interAct(); //用户交互(输入命令)
    int argCount = stringSplit(); //命令解析
    if(argCount == 0) { continue; }
    //判断和执行命令
    int n = buildinCommand(argCount);
    if(!n) normalCommand(argCount);
  }
  return 0;
}
